package xyz.riceball.usercore.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import xyz.riceball.framework.core.bean.ApiR;
import xyz.riceball.framework.core.bean.ApiRc;
import xyz.riceball.framework.core.constant.Constant;
import xyz.riceball.framework.core.context.UserContext;
import xyz.riceball.framework.core.enums.ENBool;
import xyz.riceball.framework.core.exception.RiceBallBizErrorException;
import xyz.riceball.framework.core.exception.RiceBallBizWarnException;
import xyz.riceball.framework.core.util.BeanUtils;
import xyz.riceball.framework.core.util.MessageUtils;
import xyz.riceball.framework.core.util.TransactionUtils;
import xyz.riceball.usercore.entity.po.MenuPO;
import xyz.riceball.usercore.entity.po.RoleRightPO;
import xyz.riceball.usercore.entity.po.RoutePO;
import xyz.riceball.usercore.mapper.MenuMapper;
import xyz.riceball.usercore.mapper.RoleRightMapper;
import xyz.riceball.usercore.mapper.RouteMapper;
import xyz.riceball.usercore.service.MenuService;
import xyz.riceball.usercore.utils.TreeUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜单表(Menu)表服务实现类
 *
 * @author xiaovcloud
 * @since 2022-04-24 20:17:47
 */
@Slf4j
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, MenuPO> implements MenuService {

    @Resource
    private RouteMapper routeMapper;
    @Resource
    private RoleRightMapper roleRightMapper;

    @Override
    public List<Tree<String>> treeMenu(MenuPO menuPO) {
        List<MenuPO> menuPOList = null;
        if (menuPO.getType() != null && Constant.INT1.equals(menuPO.getType())) {
            LambdaQueryWrapper<MenuPO> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(MenuPO::getType, menuPO.getType());
            menuPOList = getBaseMapper().selectList(wrapper);
        } else {
            menuPOList = getBaseMapper().selectList(null);
        }
        return TreeUtils.buildMenu(menuPOList);
    }

    @Override
    public List<Tree<String>> treeMenuCurrent() {
        String userId = UserContext.getUserId();
        List<MenuPO> menuPOList = treeMenuByUserId(userId);
        return TreeUtils.buildMenu(menuPOList);
    }

    /**
     * 根据用户id查询所有的菜单
     *
     * @param userId
     * @return
     */
    public List<MenuPO> treeMenuByUserId(String userId) {
        //获取用户权限中最下级菜单
        List<MenuPO> menuPOList = getBaseMapper().getLastMenuCurrent(userId);
        if (menuPOList.size() == 0) {
            throw new RiceBallBizErrorException("用户无权限，请联系管理员", 401);
        }
        HashSet<MenuPO> menuSet = new HashSet<>(menuPOList);
        //循环获取上级菜单
        List<String> menuIds = menuPOList.stream().map(MenuPO::getId).collect(Collectors.toList());
        List<MenuPO> allParentMenu = getBaseMapper().getAllParentMenu(menuIds);
        menuSet.addAll(allParentMenu);
        return new ArrayList<>(menuSet);
    }

    @Override
    public List<MenuPO> selectMenuLikeName(String menuName) {
        LambdaQueryWrapper<MenuPO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MenuPO::getStatus, ENBool.TRUE.getValue()).like(MenuPO::getName, menuName);
        return this.getBaseMapper().selectList(wrapper);
    }

    @Override
    public ApiR<MenuPO> add(MenuPO menuPO) {
        //新增前判断是否重复
        checkMenuBeforeAdd(menuPO);
        BeanUtils.buildAutoField(menuPO, true, UserContext.getUserId());
        int insert = this.getBaseMapper().insert(menuPO);
        return ApiR.msgData(insert == 1, "新增菜单成功", "新增菜单失败", menuPO);
    }

    /**
     * 新增前的校验
     *
     * @param menuPO
     */
    private void checkMenuBeforeAdd(MenuPO menuPO) {
        //新增，判断code是否重复
        LambdaQueryWrapper<MenuPO> wrapperCode = Wrappers.<MenuPO>lambdaQuery();
        wrapperCode.eq(MenuPO::getCode, menuPO.getCode());
        List<MenuPO> menuList = this.getBaseMapper().selectList(wrapperCode);
        if (menuList.size() > 0) {
            throw new RiceBallBizWarnException("菜单编码重复：{}", menuPO.getCode());
        }

        //新增，判断name是否重复
        LambdaQueryWrapper<MenuPO> wrapperName = Wrappers.<MenuPO>lambdaQuery();
        wrapperName.eq(MenuPO::getName, menuPO.getName());
        menuList = this.getBaseMapper().selectList(wrapperName);
        if (menuList.size() > 0) {
            throw new RiceBallBizWarnException("菜单名重复：{}", menuPO.getName()) ;
        }
    }

    @Override
    public boolean edit(MenuPO menuPO) {
        MenuPO menu = getBaseMapper().selectById(menuPO.getId());
        if (menu == null) {
            throw new RiceBallBizWarnException("通过ID：{}未能查询到菜单", menuPO.getId());
        }
        checkMenuBeforeEdit(menuPO);
        menu.setName(menuPO.getName());
        menu.setIcon(menuPO.getIcon());
        menu.setParentId(menuPO.getParentId());
        menu.setType(menuPO.getType());
        menu.setPath(menuPO.getPath());
        menu.setWeight(menuPO.getWeight());
        menu.setStatus(menuPO.getStatus());
        menu.setRemark(menuPO.getRemark());
        BeanUtils.buildAutoField(menu, false, UserContext.getUserId());
        return updateById(menu);
    }

    /**
     * 修改入库前的检测
     * @param menuPO
     */
    private void checkMenuBeforeEdit(MenuPO menuPO){
        List<MenuPO> menuList;
        //检测编码重复
        LambdaQueryWrapper<MenuPO> wrapperCode = Wrappers.<MenuPO>lambdaQuery();
        wrapperCode.eq(MenuPO::getCode, menuPO.getCode());
        menuList = this.getBaseMapper().selectList(wrapperCode);
        //修改，判断名字，除了这个id的，其他的算重复
        if (menuList.size() > 1) {
            throw new RiceBallBizWarnException("菜单编码重复：{}", menuPO.getName());
        }else if (menuList.size() == 1) {
            if (!menuList.get(0).getId().equals(menuPO.getId())) {
                throw new RiceBallBizWarnException("菜单编码重复：{}", menuPO.getName());
            }
        }

        //检测菜单名重复
        LambdaQueryWrapper<MenuPO> wrapperName = Wrappers.<MenuPO>lambdaQuery();
        wrapperName.eq(MenuPO::getName, menuPO.getName());
        menuList = this.getBaseMapper().selectList(wrapperName);
        //修改，判断名字，除了这个id的，其他的算重复
        if (menuList.size() > 1) {
            throw new RiceBallBizWarnException("菜单名称重复：{}", menuPO.getName());
        }
        if (menuList.size() == 1) {
            if (!menuList.get(0).getId().equals(menuPO.getId())) {
                throw new RiceBallBizWarnException("菜单名称重复：{}", menuPO.getName());
            }
        }
    }

    @Override
    public ApiRc<Void> delete(List<String> ids) {
        ApiRc<Void> del = ApiRc.buildResultCount(ids.size());

        for (String id : ids) {
            try {
                //1。本菜单下存在菜单，需要把这个菜单下的所有子菜单全部删除掉
                List<MenuPO> menus = getBaseMapper().getAllChildMenu(Collections.singletonList(id));
                if (CollectionUtil.isEmpty(menus)) {
                    //菜单id错误
                    del.append(MessageUtils.format("id为[{}]的菜单不存在", id));
                    continue;
                }
                List<String> childids = menus.stream().map(MenuPO::getId).collect(Collectors.toList());

                //2。删除关联的子功能
                LambdaQueryWrapper<RoutePO> wrapper = Wrappers.lambdaQuery();
                wrapper.eq(RoutePO::getMenuId, id);
                List<RoutePO> routePOList = routeMapper.selectList(wrapper);
                List<String> routeids = routePOList.stream().map(RoutePO::getId).collect(Collectors.toList());


                //3。删除子功能关联的角色
                LambdaQueryWrapper<RoleRightPO> roleRightWrapper = null;
                if (CollectionUtil.isNotEmpty(routeids)) {
                    roleRightWrapper = Wrappers.lambdaQuery();
                    roleRightWrapper.in(RoleRightPO::getRouteId, routeids);
                }

                LambdaQueryWrapper<RoleRightPO> finalRoleRightWrapper = roleRightWrapper;
                TransactionUtils.doInTransactionWithRequired(() -> {
                    if (finalRoleRightWrapper != null) {
                        roleRightMapper.delete(finalRoleRightWrapper);
                    }
                    if (!routeids.isEmpty()) {
                        routeMapper.deleteBatchIds(routeids);
                    }
                    getBaseMapper().deleteBatchIds(childids);
                    return Void.class;
                });
                del.success();
            } catch (Exception e) {
                log.error("删除菜单失败，菜单id：{}", id, e);
                del.append(MessageUtils.format("id为{}菜单删除失败:{}<br/>", id,e.getMessage()));
            }
        }
        return ApiRc.getResult(del);
    }

    @Override
    public Page<MenuPO> selectPage(Page<MenuPO> page, MenuPO menuPO) {
        LambdaQueryWrapper<MenuPO> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotEmpty(menuPO.getCode())) {
            wrapper.eq(MenuPO::getCode, menuPO.getCode());
        }
        if (StrUtil.isNotEmpty(menuPO.getCode())) {
            wrapper.like(MenuPO::getName, menuPO.getName());
        }
        return page(page, wrapper);
    }

    @Override
    public List<String> validChildMenuId() {
        return getBaseMapper().validChildMenuId();
    }

    @Override
    public boolean changeValidMenu(List<String> ids) {
        List<MenuPO> allParentMenu = getBaseMapper().getAllParentMenu(ids);
        int update = 0;
        //选中的更新为有效
        LambdaQueryWrapper<MenuPO> wrapperValid = Wrappers.<MenuPO>lambdaQuery();
        wrapperValid.in(MenuPO::getId, allParentMenu.stream().map(MenuPO::getId).collect(Collectors.toList()));
        MenuPO menuPO = new MenuPO();
        menuPO.setStatus(ENBool.TRUE.intValue());
        update += getBaseMapper().update(menuPO, wrapperValid);
        //未选中的更新为无效
        LambdaQueryWrapper<MenuPO> wrapperUnValid = Wrappers.<MenuPO>lambdaQuery();
        wrapperUnValid.notIn(MenuPO::getId, allParentMenu.stream().map(MenuPO::getId).collect(Collectors.toList()));
        menuPO.setStatus(ENBool.FALSE.intValue());
        update += getBaseMapper().update(menuPO, wrapperUnValid);

        return update > 0;
    }
}

